package com.picturehistory.core.jersey.util;

/*************************************************************************
 *  Copyright © 2007, Robert Sedgewick and Kevin Wayne. 
 *  Compilation:  javac RSA.java
 *  Execution:    java RSA N
 *  
 *  Generate an N-bit public and private RSA key and use to encrypt
 *  and decrypt a random message.
 * 
 *  % java RSA 50
 *  public  = 65537
 *  private = 553699199426609
 *  modulus = 825641896390631
 *  message   = 48194775244950
 *  encrpyted = 321340212160104
 *  decrypted = 48194775244950
 *
 *  Known bugs (not addressed for simplicity)
 *  -----------------------------------------
 *  - It could be the case that the message >= modulus. To avoid, use
 *    a do-while loop to generate key until modulus happen to be exactly N bits.
 *
 *  - It's possible that gcd(phi, publicKey) != 1 in which case
 *    the key generation fails. This will only happen if phi is a
 *    multiple of 65537. To avoid, use a do-while loop to generate
 *    keys until the gcd is 1.
 *
 *************************************************************************/

import java.math.BigInteger;
import java.security.SecureRandom;

public class RSA {

	private final static BigInteger ONE = new BigInteger("1");
	private final static SecureRandom RND = new SecureRandom();
	
	private BigInteger privateKey;
	private BigInteger publicKey;
	private BigInteger modulus;
	
	// generate an N-bit (roughly) public and private key
	public RSA(int N) {
		BigInteger p = BigInteger.probablePrime(N/2, RND);
		BigInteger q = BigInteger.probablePrime(N/2, RND);
		BigInteger phi = (p.subtract(ONE)).multiply(q.subtract(ONE));
		
		modulus = p.multiply(q); //FIX1: this should be modified
		publicKey = new BigInteger("65537"); //2^16 + 1
		privateKey = publicKey.modInverse(phi); //FIX2: this should be modified 
	}
	
	public BigInteger encrypt(BigInteger message) {
		return message.modPow(publicKey, modulus);
	}
	
	public BigInteger decrypt(BigInteger encrypted) {
		return encrypted.modPow(privateKey, modulus);
	}
}
